package com.triptogether.services;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;

import com.triptogether.utils.db.DBManager;
import com.triptogether.utils.db.models.User;
import com.triptogether.utils.log.Logger;
import com.triptogether.utils.ws.WebServiceResult;
import com.triptogether.utils.ws.WebServiceUtils;

public class ChatMessagesService extends IntentService {

	/**
	 * extra to send when calling this service to 'know' what to do.</br>
	 * send with: 'EXTRA_COMMAND_SEND_MESSAGE' or 'EXTRA_COMMAND_TRY_SEND_MESSAGES_FROM_QUEUE'
	 */
	public static final String EXTRA_COMMAND = "command";
	
	/** Command for the service to send message */
	public static final String EXTRA_COMMAND_SEND_MESSAGE = "command_send_message";
	public static final String EXTRA_MESSAGE_TEXT = "send_message";
	public static final String EXTRA_FROM_USER_FACEBOOK_ID = "from_user_facebook_id";
	public static final String EXTRA_TO_USER_FACEBOOK_ID = "to_user_facebook_id";
	
	/** command for the service to try send the messages again from the service */
	private static final String EXTRA_COMMAND_TRY_SEND_MESSAGES_FROM_QUEUE = "command_try_send_messages_from_queue";
	
	private static final int MILLIS_FOR_WAKE_UP_SERVICE = 1000 * 120;
	private final String TAG = Logger.makeLogTag(this.getClass().getName());	
	private ServicesQueuesManager messagesQueueManagerInstance;

	public ChatMessagesService() {
	    super(ChatMessagesService.class.getName());
		messagesQueueManagerInstance = ServicesQueuesManager.getInstance();
    }

	@Override
	protected void onHandleIntent(Intent intent) {
		String extraCommand = intent.getStringExtra(EXTRA_COMMAND);
		if(extraCommand.equals(EXTRA_COMMAND_SEND_MESSAGE)) {
			sendMessage(intent);
		} else if (extraCommand.equals(EXTRA_COMMAND_TRY_SEND_MESSAGES_FROM_QUEUE)) {
			sendMessagesFromQueue();
		} else {
			Logger.LOGW(TAG, "extra 'EXTRA_COMMAND' not sent with the intent to " +
			                 ChatMessagesService.class.getName() + "\n" +
			                 "or the extra does not exsits");
		}
	}
	
	/**
	 * This method get the intent from onHandleIntent and get the extra from it.
	 * than create the message in the DB, and send the message with a webservice 
	 * @param intent The intent from onHandleIntent
	 */
	private void sendMessage(Intent intent) {
		// Get the extras details of the message from the intent
		final String fromUserFacebookId = intent.getStringExtra(EXTRA_FROM_USER_FACEBOOK_ID);
		final String toUserFacebookId = intent.getStringExtra(EXTRA_TO_USER_FACEBOOK_ID);
		final String message = intent.getStringExtra(EXTRA_MESSAGE_TEXT);
		
		// Get instance of DBManager
		DBManager dbManagerInstance = DBManager.getInstance(getApplicationContext());
		
		// Find the user in the DB
		User userChatWith = dbManagerInstance.findUserByFacebookId(toUserFacebookId);

		// Create a message in the DB
		dbManagerInstance.createMessage(true, userChatWith, message,
		        Calendar.getInstance().getTime());

		// send the message with asynctask to not delay the UI
		new AsyncTask<Void, Void, Void>() {
			@Override
			protected Void doInBackground(Void... params) {
				// Try to send the message with the webservice
				WebServiceResult sendChatMessage = WebServiceUtils.sendChatMessage(
				        fromUserFacebookId, toUserFacebookId, message);

				if (!sendChatMessage.isStatusOK()) {
					// If the status from the webService is not OK, mean the
					// message
					// failed to send, adding the message to a queue and try
					// again later
					messagesQueueManagerInstance.addChatMessageToQueue( fromUserFacebookId, toUserFacebookId, message);

					setAlarm();
				}
				return null;
			}
		}.execute(new Void[] {});
	}
	
	/**
	 * The method manage the sending messages from the messages queue.</br>
	 * any message that failed to be sent, go to the
	 * {@link ServicesQueuesManager} and here in the method we handle this 'Failed' messages Queue
	 */
	private void sendMessagesFromQueue() {
		List<Map<String, String>> failedMessages = new ArrayList<Map<String,String>>();
		while(!messagesQueueManagerInstance.isChatMessagesQueueEmpty()) {
			Map<String, String> messageMap = messagesQueueManagerInstance.getChatMessageAndRemove();
			String fromUserFacebookId = messageMap.get(ServicesQueuesManager.KEY_FROM_USER);
			String toUserFacebookId = messageMap.get(ServicesQueuesManager.KEY_TO_USER);
			String message = messageMap.get(ServicesQueuesManager.MESSAGE_KEY);
			
			// Try to send the message
			WebServiceResult sendChatMessage = WebServiceUtils.sendChatMessage(fromUserFacebookId, toUserFacebookId, message);
			
			if (!sendChatMessage.isStatusOK()) {
				failedMessages.add(messageMap);	
			}
		}
		
		if (!failedMessages.isEmpty()) {
			for (Map<String, String> map : failedMessages) {
				// Add all the failed messages again to the queue
				messagesQueueManagerInstance.addChatMessageToQueue(map);
			}

			setAlarm();
		}
	}

	/**
	 * This method sets alram to wake up the {@link ChatMessagesService} and
	 * send messages that failed to send
	 */
	private void setAlarm() {
	    // Set an alarmManager to try sending the messages again...
	    AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(
	            Context.ALARM_SERVICE);

	    // Create the intent taht will wake up this service
	    Intent wakeSendMessaegsService = new Intent(getApplicationContext(),
	            ChatMessagesService.class);
	    wakeSendMessaegsService.putExtra(EXTRA_COMMAND,
	            EXTRA_COMMAND_TRY_SEND_MESSAGES_FROM_QUEUE);

	    // Create pending intent from the intent
	    PendingIntent pi = PendingIntent.getService(getApplicationContext(), 0,
	            wakeSendMessaegsService, 0);

	    // Set the alaram to wake up in 'MILLIS_FOR_WAKE_UP_SERVICE' milliseconds
		alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
	            System.currentTimeMillis(), MILLIS_FOR_WAKE_UP_SERVICE, pi);
    }
}
